﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Controls;

using PlanningGIS.ArcGIS;
using PlanningGIS.Util.Log;
using PlanningGIS.Engine.Controls;


namespace PlanningGIS.DataCheck.model
{
    /// <summary>
    /// 框架和基础要素对比检查
    /// </summary>
    public class FrameMatchCheck : Check
    {
        public IActiveView pAV;//测试用

        /// <summary>
        /// 检查
        /// </summary>
        /// <param name="errType">错误类型</param>
        /// <param name="pFeatLyrFrame">框架图层</param>
        /// <param name="pFeatLyrBase">基础图层</param>
        /// <param name="pGeometry">检查范围，若是null，则为全图范围</param>
        /// <param name="buffer">所开缓冲大小，若是0，则不开缓冲</param>
        /// <param name="fieldNameFrame">框架数据的属性字段名，若是""（空字符串），则不考虑属性对应关系</param>
        /// <param name="fieldNameBase">基础数据的属性字段名，若是""（空字符串），则不考虑属性对应关系</param>
        /// <returns></returns>
        public int Check(string errType, IFeatureLayer pFeatLyrFrame, IFeatureLayer pFeatLyrBase, IGeometry pGeometry,
            double buffer, List<string> fieldNameFrame, List<string> fieldNameBase)
        {
            IFeatureClass pFeatClsFrame = pFeatLyrFrame.FeatureClass;
            IFeatureClass pFeatClsBase = pFeatLyrBase.FeatureClass;

            #region 条件判断
            if (pFeatClsFrame.FeatureType != pFeatClsBase.FeatureType)
            {
               LogHelper.WriteErrLog("框架数据与基础数据的要素类型不一致，无法进行检查！");
                return -1;
            }

            if (pFeatClsFrame.ShapeType != pFeatClsBase.ShapeType)
            {
               LogHelper.WriteErrLog("框架数据与基础数据的几何类型不一致，无法进行检查！");
                return -1;
            }

            if (buffer < 0)
            {
               LogHelper.WriteErrLog("缓冲值必须为非负数！");
                return -1;
            }

            #region 字段条件
            int idxFrame, idxBase;
            IField pFieldFrame = null, pFieldBase = null;
            List<int> FieldFrameIndexs = new List<int>();
            List<int> FieldBaseIndexs = new List<int>();
            for (int i = 0; i < fieldNameBase.Count; i++)
            {
                if (fieldNameFrame[i].Trim() == "" || fieldNameBase[i].Trim() == "")
                {
                    idxFrame = -1;
                    idxBase = -1;
                }
                else
                {
                    idxFrame = pFeatClsFrame.FindField(fieldNameFrame[i]);
                    if (idxFrame == -1)
                    {
                       LogHelper.WriteErrLog("[" + pFeatLyrFrame.Name + "]图层中未找到[" + fieldNameFrame[i] + "]字段，无法进行检查！");
                        continue;
                    }

                    idxBase = pFeatClsBase.FindField(fieldNameFrame[i]);
                    if (idxBase == -1)
                    {
                       LogHelper.WriteErrLog("[" + pFeatLyrBase.Name + "]图层中未找到[" + fieldNameBase[i] + "]字段，无法进行检查！");
                        continue;
                    }

                    pFieldFrame = pFeatClsFrame.Fields.get_Field(idxFrame);
                    pFieldBase = pFeatClsBase.Fields.get_Field(idxBase);

                    if (pFieldFrame.Type != pFieldBase.Type)
                    {
                       LogHelper.WriteErrLog("两个数据所设置的字段[" + pFeatLyrFrame.Name + "]、[" + fieldNameBase[i] + "] 类型不一致，无法进行检查！");
                        continue;
                    }

                    if ((int)pFieldBase.Type > 4)
                    {
                       LogHelper.WriteErrLog("所选字段必须为数值或者字符型！");
                        continue;
                    }

                    FieldFrameIndexs.Add(idxFrame);
                    FieldBaseIndexs.Add(idxBase);
                }
            }
            #endregion

            #endregion

            ISpatialFilter pSpatialFileter = new SpatialFilterClass();
            if (pGeometry != null) pSpatialFileter.Geometry = pGeometry;

            pSpatialFileter.SpatialRel = esriSpatialRelEnum.esriSpatialRelIntersects;
            pSpatialFileter.GeometryField = pFeatClsFrame.ShapeFieldName;

            showFakePro(true);
            LogHelper.WriteLog("正在计算需要检查的要素数目...");

            int max = FeatureClassHelper.GetFeatureCountFromLayer(pFeatLyrFrame, pSpatialFileter);// pFeatClsFrame.FeatureCount(pSpatialFileter);

            LogHelper.WriteLog("计算完毕。共有" + max + "个要素参与检查。");
            showFakePro(false);

            if (max == 0)
            {
                LogHelper.WriteLog("没有满足检查要求的要素存在。");
                return -1;
            }

            setProcessBarMax(max);

            IFeatureCursor pFeatCur = pFeatLyrFrame.Search(pSpatialFileter, false);

            int id;
            int count = 0;      //框架数据要素总数
            int Scancount = 0;  //每个框架要素能够匹配的基础要素总数
            int errCount = 0;   //对比出错记录数
            IFeature pFeatureFrame;
            List<int> AllIDs = new List<int>();     //所有已匹配的基础要素的OID集合

            IPoint outPoint = new PointClass();     //基础数据中线图形的中间点
            IPoint outPoint1 = new PointClass();    //基础数据中线图形的1/5点
            IPoint outPoint2 = new PointClass();    //基础数据中线图形的4/5点

            while ((pFeatureFrame = pFeatCur.NextFeature()) != null)
            {
                if (IsStop()) break;

                count++;
                setProcessBarCurrent(count);

                IGeometry pShapeCopyFrame = pFeatureFrame.ShapeCopy;
                if (pShapeCopyFrame == null || pShapeCopyFrame.IsEmpty) continue;

                IGeometry pBuffer = GeometryHelper.GetBuffer(pShapeCopyFrame, buffer);

                //-----------------------------------------------调试用
                //DisplayUtility.DrawPolygonXOR(pBuffer as IPolygon, pAV);
                //----------------------------------------------------

                ISelectionSet pSelSetBase = CursorHelper.GetSelset(pFeatLyrBase as IFeatureSelection, pBuffer, "",
                    esriSpatialRelEnum.esriSpatialRelIntersects);

                if (pSelSetBase.Count == 0)
                {
                    RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, null, pFeatureFrame.ShapeCopy, "缺失基础数据");
                    errCount++;
                    continue;
                }

                IEnumIDs pIDs = pSelSetBase.IDs;
                pIDs.Reset();

                double tempDist1, tempDist2, tempDist3, tempDist4;

                Scancount = 0;
                IProximityOperator proximityOp = pShapeCopyFrame as IProximityOperator;
                //pIDs.Reset();
                while ((id = pIDs.Next()) != -1)
                {
                    IFeature pFeatureBase = pFeatClsBase.GetFeature(id);
                    IPolyline pLine = pFeatureBase.ShapeCopy as IPolyline;

                    List<int> ErrIndexs = new List<int>();
                    List<IPoint> InsertPoints = new List<IPoint>();
                    
                    //如果首点、尾点、中间点落在框架道路上，则认为两者为相同道路
                    pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length / 5, false, outPoint1);
                    pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length / 2, false, outPoint);
                    pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length * 4 / 5, false, outPoint2);
                    if ((proximityOp.ReturnDistance(outPoint1) < buffer) && (proximityOp.ReturnDistance(outPoint) < buffer) && (proximityOp.ReturnDistance(outPoint2) < buffer))
                    {
                        Scancount++;
                        AllIDs.Add(id);
                        for (int i = 0; i < pLine.Length; i += 50)
                        {
                            pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, i, false, outPoint);
                            InsertPoints.Add(outPoint);

                            if (proximityOp.ReturnDistance(outPoint) > buffer)
                            {
                                ITopologicalOperator2 pTopo = pLine as ITopologicalOperator2;
                                pTopo.IsKnownSimple_2 = false;
                                pTopo.Simplify();
                                ITopologicalOperator pTopoOp = pLine as ITopologicalOperator;
                                IGeometry pDiffGeom = pTopoOp.Difference(pShapeCopyFrame);
                                if (pDiffGeom.IsEmpty)
                                {
                                    //pDiffGeom = pTopoOp.SymmetricDifference(pLine);
                                }
                                else
                                {
                                    RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                        pDiffGeom, "该处图形不匹配");
                                    errCount++;
                                }
                                break;
                            }

                            if (((pLine.Length - i) < 50) & (proximityOp.ReturnDistance(pLine.ToPoint) > buffer))
                            {
                                ITopologicalOperator2 pTopo = pLine as ITopologicalOperator2;
                                pTopo.IsKnownSimple_2 = false;
                                pTopo.Simplify();
                                ITopologicalOperator pTopoOp = pLine as ITopologicalOperator;
                                IGeometry pDiffGeom = pTopoOp.Difference(pShapeCopyFrame);
                                if (pDiffGeom.IsEmpty)
                                {
                                    //pDiffGeom = pTopoOp.SymmetricDifference(pLine);
                                }
                                else
                                {
                                    RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                        pDiffGeom, "该处图形不匹配");
                                    errCount++;
                                }
                                break;
                            }
                        }

                        //如果属性为空，则放宽对属性的约束；否则误查太多
                        if (FieldFrameIndexs.Count > 0)
                        {
                            string errField = CheckFeatureAttribute(pFeatureFrame, pFeatureBase, FieldFrameIndexs, FieldBaseIndexs);
                            if (errField.EndsWith("、")) errField = errField.Substring(0, errField.Length - 1);
                            if (errField != "")
                            {
                                RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                    pFeatureBase.ShapeCopy, "[" + errField + "]字段不匹配");
                                errCount++;
                            }
                        }
                    }
                }

                //如果找不到基础数据，则可能是基础数据存在，但是比框架数据长，所以系统放宽检索条件，再次匹配
                if (Scancount == 0)
                {
                    IPoint outPoint3 = new PointClass();    //基础数据中线图形的1/3点
                    IPoint outPoint4 = new PointClass();    //基础数据中线图形的2/3点
                    List<IPoint> MatchPoints = new List<IPoint>();

                    pIDs.Reset();
                    while ((id = pIDs.Next()) != -1)
                    {
                        IFeature pFeatureBase = pFeatClsBase.GetFeature(id);
                        IPolyline pLine = pFeatureBase.ShapeCopy as IPolyline;

                        List<int> ErrIndexs = new List<int>();
                        List<IPoint> InsertPoints = new List<IPoint>();

                        //如果首点、尾点、中间点落在框架道路上，则认为两者为相同道路
                        pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length / 5, false, outPoint1);
                        pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length / 3, false, outPoint3);
                        pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length / 2, false, outPoint);
                        pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length * 4 / 5, false, outPoint2);
                        pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, pLine.Length * 2 / 3, false, outPoint4);
                        MatchPoints.Add(pLine.FromPoint);
                        MatchPoints.Add(outPoint1);
                        MatchPoints.Add(outPoint3);
                        MatchPoints.Add(outPoint);
                        MatchPoints.Add(outPoint2);
                        MatchPoints.Add(outPoint4);
                        MatchPoints.Add(pLine.ToPoint);

                        bool IsLineMatched = false;     //判断两线是否匹配
                        for (int i = 1; i < MatchPoints.Count; i++)
                        {
                            if ((proximityOp.ReturnDistance(MatchPoints[i - 1]) < 0.01) && (proximityOp.ReturnDistance(MatchPoints[i]) < 0.01))
                            {
                                IsLineMatched = true;
                                break;
                            }
                        }

                        if (IsLineMatched)
                        {
                            Scancount++;
                            AllIDs.Add(id);
                            for (int i = 0; i < pLine.Length; i += 50)
                            {
                                pLine.QueryPoint(esriSegmentExtension.esriExtendAtFrom, i, false, outPoint);
                                InsertPoints.Add(outPoint);

                                if (proximityOp.ReturnDistance(outPoint) > buffer)
                                {
                                    ITopologicalOperator2 pTopo = pLine as ITopologicalOperator2;
                                    pTopo.IsKnownSimple_2 = false;
                                    pTopo.Simplify();
                                    ITopologicalOperator pTopoOp = pLine as ITopologicalOperator;
                                    IGeometry pDiffGeom = pTopoOp.Difference(pShapeCopyFrame);
                                    if (pDiffGeom.IsEmpty)
                                    {
                                        //pDiffGeom = pTopoOp.SymmetricDifference(pLine);
                                    }
                                    else
                                    {
                                        RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                            pDiffGeom, "该处图形不匹配");
                                        errCount++;
                                    }
                                    break;
                                }

                                if (((pLine.Length - i) < 50) & (proximityOp.ReturnDistance(pLine.ToPoint) > buffer))
                                {
                                    ITopologicalOperator2 pTopo = pLine as ITopologicalOperator2;
                                    pTopo.IsKnownSimple_2 = false;
                                    pTopo.Simplify();
                                    ITopologicalOperator pTopoOp = pLine as ITopologicalOperator;
                                    IGeometry pDiffGeom = pTopoOp.Difference(pShapeCopyFrame);
                                    if (pDiffGeom.IsEmpty)
                                    {
                                        //pDiffGeom = pTopoOp.SymmetricDifference(pLine);
                                    }
                                    else
                                    {
                                        RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                            pDiffGeom, "该处图形不匹配");
                                        errCount++;
                                    }
                                    break;
                                }
                            }

                            //如果属性为空，则放宽对属性的约束；否则误查太多
                            if (FieldFrameIndexs.Count > 0)
                            {
                                string errField = CheckFeatureAttribute(pFeatureFrame, pFeatureBase, FieldFrameIndexs, FieldBaseIndexs);
                                if (errField.EndsWith("、")) errField = errField.Substring(0, errField.Length - 1);
                                if (errField != "")
                                {
                                    RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, pFeatureBase,
                                        pFeatureBase.ShapeCopy, "[" + errField + "]字段不匹配");
                                    errCount++;
                                }
                            }
                        }
                    }

                    //如果没有匹配的基础数据，则输出为错误
                    if (Scancount == 0)
                    {
                        RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, pFeatureFrame, null, pFeatureFrame.ShapeCopy, "缺失基础数据");
                        errCount++;
                    }
                }

                Marshal.ReleaseComObject(pIDs);
                pIDs = null;

                (pFeatLyrBase as IFeatureSelection).Clear();
            }

            Marshal.ReleaseComObject(pFeatCur);
            pFeatCur = null;

            //如果还有未匹配的基础数据，则输出为"缺失框架数据"
            AllIDs.Sort();
            pFeatCur = pFeatClsBase.Search(pSpatialFileter, false);
            IFeature pFeatureScan;
            while ((pFeatureScan = pFeatCur.NextFeature()) != null)
            {
                if (IsStop()) break;

                if (AllIDs.Contains(pFeatureScan.OID) == false)
                {
                    RecordListHelper.AddRecordItem(errType, pFeatLyrFrame, pFeatLyrBase, null, pFeatureScan, pFeatureScan.ShapeCopy, "缺失框架数据");
                }
            }

            Marshal.ReleaseComObject(pFeatCur);
            pFeatCur = null;

            return errCount;
        }

        /// <summary>
        /// 两个要素的属性对比
        /// </summary>
        /// <param name="pFeatFrame">框架要素</param>
        /// <param name="pFeatBase">基础要素</param>
        /// <returns>属性是否一致</returns>
        private string CheckFeatureAttribute(IFeature pFeatFrame, IFeature pFeatBase, List<int> FieldFrameIndexs, List<int> FieldBaseIndexs)
        {
            string UnMatchedFields = "";
            IField pField;
            int j;
            object value1, value2;
            int i = 0;
            for (int m = 0; m < FieldFrameIndexs.Count; m++)
            {
                i = FieldFrameIndexs[m];
                pField = pFeatFrame.Fields.get_Field(i);
                if (pField.Type == esriFieldType.esriFieldTypeGeometry) continue;
                if (!pField.Editable) continue;//对不可编辑的字段（一般都是系统字段）跳过

                j = FieldBaseIndexs[m];
                if (pField.Type != pFeatBase.Fields.get_Field(j).Type) return pField.Name;//类型不匹配，也不一致

                //都为空，则跳过
                if ((pFeatFrame.get_Value(i) == null || pFeatFrame.get_Value(i) == DBNull.Value) &&
                    (pFeatBase.get_Value(j) == null || pFeatBase.get_Value(j) == DBNull.Value)) continue;

                value1 = pFeatFrame.get_Value(i);
                value2 = pFeatBase.get_Value(j);

                if (pField.Type == esriFieldType.esriFieldTypeString)
                {
                    //对于字符串型的字段，空(null)、空字符串("")以及"0"都表示一个意思
                    if (value1 == null || value1 == DBNull.Value || value1.ToString() == "0") value1 = "";
                    if (value2 == null || value2 == DBNull.Value || value2.ToString() == "0") value2 = "";
                }
                else
                {
                    //对于不是字符串型的字段，只要有一个为空，则返回false（如果两个同时为空，前面已经判断过，会直接跳过）
                    if (value1 == null || value1 == DBNull.Value) return pField.Name;
                    if (value2 == null || value2 == DBNull.Value) return pField.Name;
                }

                //对于浮点型的字段，考虑到精度问题
                if (pField.Type == esriFieldType.esriFieldTypeSingle || pField.Type == esriFieldType.esriFieldTypeDouble)
                {
                    double d1 = Double.Parse(value1.ToString());
                    double d2 = double.Parse(value2.ToString());
                    if (Math.Abs(d1 - d2) < 0.0001) continue;
                }

                //只要有一对不一致，则属性不一致
                if (!value1.Equals(value2)) UnMatchedFields += pField.Name + "、";

            }
            return UnMatchedFields;
        }
    }
}
